package com.k.kpp.util.rpc.netty.udp.client;

import com.k.kpp.util.rpc.netty.model.DataChangeProtocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * UDP如何实现可靠传输？
 *
 * UDP要想可靠，就要接收方收到UDP之后回复个确认包，发送方有个机制，收不到确认包就要重新发送，每个包有递增的序号，
 * 接收方发现中间丢了包就要发重传请求，当网络太差时候频繁丢包，防止越丢包越重传的恶性循环，
 * 要有个发送窗口的限制，发送窗口的大小根据网络传输情况调整，调整算法要有一定自适应性。
 * 恭喜你， 你在应用层重新实现了TCP！
 *
 * https://www.zhihu.com/question/283995548?sort=created
 *
 * @author k
 */
public class UDPNettyClient {

    public static String udpSend(String host,Integer port, String id,String msg,long timeout){
        String responseData=null;
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            UdpClientHandlerOne channelHandler = new UdpClientHandlerOne(id);
            channelHandler.setRequestId(id);
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .handler(channelHandler);
            Channel channel = bootstrap.bind(0).sync().channel();

            InetSocketAddress address = new InetSocketAddress(host, port);
            ByteBuf byteBuf = Unpooled.copiedBuffer(msg.getBytes(StandardCharsets.UTF_8));
            channel.writeAndFlush(new DatagramPacket(byteBuf, address)).sync();
            // 阻塞timeout时间获取结果
            responseData = channelHandler.getResponseDataWait(timeout, TimeUnit.MILLISECONDS);
//            if (!channel.closeFuture().await(4000)) {
//                System.out.println("查询超时！");
//            }
            channel.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
        return responseData;
    }

    public static void main(String[] args) {
        String ip = "10.110.12.28";
        Integer port = 22409;
        System.out.println("client start send:");
        for(int i=0;i<10;i++){
            final String id = i+"";
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String protocol = DataChangeProtocol.getProtocelDataUpdateReq(id);
                    // 可能阻塞,所以在线程中处理
                    String result = udpSend(ip,port,id,protocol,3000);
                    // 业务
                    System.out.println("requestId:"+id+" result:"+result);
                }
            }).start();
        }
        System.out.println("client end:");
    }
}
